package org.jheuristics.ga.operators.selectors;

import java.util.Iterator;

import org.jheuristics.Individual;
import org.jheuristics.ga.GAConfig;
import org.jheuristics.ga.GAStatus;
import org.jheuristics.ga.Population;
import org.jheuristics.ga.multiObjective.MultiObjectiveFitness;
import org.jheuristics.ga.operators.Selector;
import org.jheuristics.ga.transformations.FitnessToDoubleTransformation;

/**
 * TODO
 *
 * @author Marcell Manfrin, marcell@ourgrid.org, Oct 31, 2005
 */
public class WeightedSelector implements Selector {
	private double[] weights;
	private FitnessToDoubleTransformation[] transformations;

	/**
	 * TODO
	 *
	 * @param transformation
	 * @param weights
	 */
	public WeightedSelector(FitnessToDoubleTransformation[] transformations, double[] weights) {
		setTransformations(transformations);
		setWeights(weights);
	}

	/**
	 * TODO
	 *
	 * @param population
	 * @param howMany
	 * @param status
	 * @param config
	 * @return
	 * @see org.jheuristics.ga.operators.Selector#select(org.jheuristics.ga.Population, int, org.jheuristics.ga.GAStatus, org.jheuristics.ga.GAConfig)
	 */
	public Individual[] select(Population population, int howMany, GAStatus status, GAConfig config) {
        if (weights.length != transformations.length) {
			// TODO: ERROR MSG
        	throw new IllegalStateException();
        }
        if (null == population) {
			// TODO: ERROR MSG
			throw new NullPointerException();
		}
        if (0 == population.size()) {
        	return new Individual[0];
        }

        double[][] values = new double[population.size()][getTransformations().length];
		{
			int i = 0;
	        for (Iterator it = population.iterator(); it.hasNext(); i++) {
	        	Comparable[] fitnesses = ((MultiObjectiveFitness) ((Individual) it.next()).getFitness()).getObjectives();
	        	for (int j = 0; j < getTransformations().length; j++) {
	        		values[i][j] = getTransformations()[j].toDouble(fitnesses[j]);
	        	}
	        }
			double[] min = new double[values[0].length];
			double[] max = new double[values[0].length];
			for (int j = 0; j < values[0].length; j++) {
				min[j] = values[0][j];
				max[j] = values[0][j];
			}
			for (i = 1; i < values.length; i++) {
				for (int j = 0; j < values[i].length; j++) {
					if (min[j] > values[i][j]) {
						min[j] = values[i][j];
					}
					if (max[j] < values[i][j]) {
						max[j] = values[i][j];
					}
				}
			}
			for (i = 0; i < values.length; i++) {
				for (int j = 0; j < values[i].length; j++) {
					values[i][j] = Math.abs(values[i][j] - min[j]) / (max[j] - min[j]);
				}
			}
		}

		double weighted[] = new double[population.size()];
		for (int i = 0; i < values.length; i++) {
			weighted[i] = 0.0;
			for (int j = 0; j < values[i].length; j++) {
				weighted[i] += weights[j] * values[i][j];
			}
		}

		int result = 0;
		double min = weighted[0];
		for (int i = 0; i < values.length; i++) {
			if (weighted[i] < min) {
				result = i;
				min = weighted[i];
			}
		}
		return new Individual[] {population.getIndividual(result)};
	}

	/**
	 * TODO
	 *
	 * @return
	 */
	public double[] getWeights() {
		return weights;
	}

	/**
	 * TODO
	 *
	 * @param weights
	 */
	public void setWeights(double[] weights) {
		if (null == weights) {
			// TODO: ERROR MSG
			throw new NullPointerException();
		}
		this.weights = weights;
	}

	/**
	 * TODO
	 *
	 * @return
	 */
	public FitnessToDoubleTransformation[] getTransformations() {
		return transformations;
	}


	/**
	 * TODO
	 *
	 * @param transformation
	 */
	public void setTransformations(FitnessToDoubleTransformation[] transformations) {
		if (null == transformations) {
			// TODO: ERROR MSG
			throw new NullPointerException();
		}
		for (int i = 0; i < transformations.length; i++) {
			if (null == transformations[i]) {
				// TODO: ERROR MSG
				throw new NullPointerException();
			}
		}
		this.transformations = transformations;
	}

}
